#include "parser.h"
#include "frontend.h"
#include "tokenizer.h"
#include <fstream>
#include <iostream>
#include <limits>
#include <regex>
#include <sstream>
#include <stdexcept>

using StructRefMap = std::unordered_map<std::string, std::list<std::string>>;
static StructRefMap refMap;

std::string Parser::workingPath_ = "";
std::vector<std::string> Parser::includePathlist_;

std::string Parser::getTokenError(Token &token, std::ostream &os) {
  std::stringstream ss;
  ss << "-> " << token.getFile() << " ( " << token.getLineno() << ","
     << token.getCol() << " ): ";
  return ss.str();
}

Parser::~Parser() {
  for (auto *tokenizer : tokenizerList_) {
    delete tokenizer;
  }
  tokenizerList_.clear();
}

bool Parser::analyze(const std::string &file, std::ostream &os,
                     const std::string &searchpath) {
  std::stringstream ss;
  try {
    return analyze_unsafe(file, os, searchpath);
  } catch (std::runtime_error &rex) {
    ss << getTokenError(curTokenizer_->getCurToken(), os) << rex.what();
    curTokenizer_->printLine(curTokenizer_->getCurToken(), ss);
    error_ = ss.str();
    os << error_;
    return false;
  } catch (std::exception &ex) {
    ss << getTokenError(curTokenizer_->getCurToken(), os) << ex.what();
    curTokenizer_->printLine(curTokenizer_->getCurToken(), ss);
    error_ = ss.str();
    os << error_;
    return false;
  } catch (...) {
    return false;
  }
  return true;
}

bool Parser::analyze_unsafe(const std::string &file, std::ostream &os,
                            const std::string &searchpath) {
  Tokenizer *tokenizer = new Tokenizer(file);
  tokenizerList_.push_back(tokenizer);
  curTokenizer_ = tokenizer;
  curFile_ = file;
  return parse(file, *tokenizer, os, searchpath);
}

bool Parser::doChunk(std::istream &is, Tokenizer &tokenizer, std::ostream &os,
                     bool raiseError) {
  auto &token = tokenizer.getNext(is);
  if (!token) {
    return true;
  }
  switch (token.getType()) {
  case TokenType::KEYWORD_STRUCT:
    doStruct(is, tokenizer, os);
    break;
  case TokenType::KEYWORD_SERVICE:
    doService(is, tokenizer, os);
    break;
  case TokenType::TERM_DOUBLE_SLASH:
    doSingleComment(is, tokenizer);
    break;
  case TokenType::TERM_COMMENT_BEGIN:
    doMultiComment(is, tokenizer);
    break;
  case TokenType::TERM_HASH:
    doImport(is, tokenizer, os);
    break;
  case TokenType::TERM_BLANK:
    break;
  case TokenType::KEYWORD_ENUM:
    doEnum(is, tokenizer);
    break;
  default:
    if (raiseError) {
      throw std::runtime_error("Error, unknown identifier");
    }
    return false;
  }
  return true;
}

std::string ConvertPath(const std::string &filedir) {
  char dir[1024] = {};

#ifdef _WIN32
  char *res = _fullpath(dir, filedir.c_str(), 1024);
  if (res == nullptr) {
    return "";
  }
#else
  realpath(filedir.c_str(), dir);
#endif
  return dir;
}

bool Parser::parse(const std::string &file, Tokenizer &tokenizer,
                   std::ostream &os, const std::string &searchpath) {
  std::fstream fs;
  std::string fullpath = frontend_->concatPath(searchpath, file);
  fs.open(file, std::ios::in);
  if (!fs) {
    throw std::runtime_error("File not found:" + file);
  }
  fileSet_.emplace(file);
  while (!fs.fail()) {
    if (!doChunk(fs, tokenizer, os, true)) {
      return false;
    }
  }
  return true;
}

bool isContKey(ValueType type) {
  switch (type) {
  case ValueType::STRING:
  case ValueType::ENUM:
  case ValueType::I8:
  case ValueType::I16:
  case ValueType::I32:
  case ValueType::I64:
  case ValueType::UI8:
  case ValueType::UI16:
  case ValueType::UI32:
  case ValueType::UI64:
    return true;
  default:
    break;
  }
  return false;
}

bool Parser::validateStructField(std::ostream &os, IdlNode &field,
                                 std::list<std::string> &ref) {
  if (field.type == ValueType::DICT &&
      field.second->type == ValueType::STRUCT) {
    // dict<x, target>
    auto &token = field.second->token;
    if (structSet_.find(token.getText()) == structSet_.end()) {
      os << getTokenError(token, os) << "User defined type: " << token.getText()
         << " not found";
      token.printLine(os);
      os << std::endl;
      validationFailed_ = true;
    } else {
      ref.push_back(token.getText());
    }
  } else if (field.type == ValueType::STRUCT) {
    auto &token = field.token;
    if (structSet_.find(token.getText()) == structSet_.end()) {
      os << getTokenError(token, os) << "User defined type: " << token.getText()
         << " not found";
      token.printLine(os);
      os << std::endl;
      validationFailed_ = true;
    } else {
      ref.push_back(token.getText());
    }
  } else if (field.type == ValueType::SEQ &&
             field.first->type == ValueType::STRUCT) {
    auto &token = field.first->token;
    if (structSet_.find(token.getText()) == structSet_.end()) {
      os << getTokenError(token, os) << "User defined type: " << token.getText()
         << " not found";
      token.printLine(os);
      os << std::endl;
      validationFailed_ = true;
    } else {
      ref.push_back(token.getText());
    }
  }
  // Check enum
  if (field.type == ValueType::SEQ && field.first->type == ValueType::ENUM) {
    auto &token = field.first->token;
    if (!isEnum(token.getText())) {
      os << getTokenError(token, os) << "User defined type: " << token.getText()
         << " not found";
      token.printLine(os);
      os << std::endl;
      validationFailed_ = true;
    }
  } else if (field.type == ValueType::DICT &&
             field.first->type == ValueType::ENUM) {
    auto &token = field.first->token;
    if (!isEnum(token.getText())) {
      os << getTokenError(token, os) << "User defined type: " << token.getText()
         << " not found";
      token.printLine(os);
      os << std::endl;
      validationFailed_ = true;
    }
  } else if (field.type == ValueType::SET &&
             field.first->type == ValueType::ENUM) {
    auto &token = field.first->token;
    if (!isEnum(token.getText())) {
      os << getTokenError(token, os) << "User defined type: " << token.getText()
         << " not found";
      token.printLine(os);
      os << std::endl;
      validationFailed_ = true;
    }
  } else if (field.type == ValueType::ENUM) {
    auto &token = field.token;
    if (!isEnum(token.getText())) {
      os << getTokenError(token, os) << "User defined type: " << token.getText()
         << " not found";
      token.printLine(os);
      os << std::endl;
      validationFailed_ = true;
    }
  }
  return true;
}

bool Parser::Parser::validateAllStruct(std::ostream &os) {
  for (auto &stru : structList_) {
    for (auto &field : stru.field_list) {
      validateStructField(os, field, refMap[stru.name]);
    }
  }
  // TODO REF MAP
  return true;
}

bool Parser::validateMethodArgument(std::ostream &os, IdlNode &arg) {
  if (arg.type == ValueType::STRUCT) {
    auto &token = arg.token;
    if (structSet_.find(token.getText()) == structSet_.end()) {
      os << getTokenError(token, os) << "User defined type: " << token.getText()
         << " not found";
      token.printLine(os);
      os << std::endl;
      validationFailed_ = true;
    }
  } else if (arg.type == ValueType::DICT &&
             arg.second->type == ValueType::STRUCT) {
    // dict<x, target>
    auto &token = arg.second->token;
    if (structSet_.find(token.getText()) == structSet_.end()) {
      os << getTokenError(token, os) << "User defined type: " << token.getText()
         << " not found";
      token.printLine(os);
      os << std::endl;
      validationFailed_ = true;
    }
  } else if (arg.type == ValueType::SEQ &&
             arg.first->type == ValueType::STRUCT) {
    auto &token = arg.first->token;
    if (structSet_.find(token.getText()) == structSet_.end()) {
      os << getTokenError(token, os) << "User defined type: " << token.getText()
         << " not found";
      token.printLine(os);
      os << std::endl;
      validationFailed_ = true;
    }
  }
  // Check enum
  if (arg.type == ValueType::SEQ && arg.first->type == ValueType::ENUM) {
    auto &token = arg.first->token;
    if (!isEnum(token.getText())) {
      os << getTokenError(token, os) << "User defined type: " << token.getText()
         << " not found";
      token.printLine(os);
      os << std::endl;
      validationFailed_ = true;
    }
  } else if (arg.type == ValueType::DICT &&
             arg.first->type == ValueType::ENUM) {
    auto &token = arg.first->token;
    if (!isEnum(token.getText())) {
      os << getTokenError(token, os) << "User defined type: " << token.getText()
         << " not found";
      token.printLine(os);
      os << std::endl;
      validationFailed_ = true;
    }
  } else if (arg.type == ValueType::SET && arg.first->type == ValueType::ENUM) {
    auto &token = arg.first->token;
    if (!isEnum(token.getText())) {
      os << getTokenError(token, os) << "User defined type: " << token.getText()
         << " not found";
      token.printLine(os);
      os << std::endl;
      validationFailed_ = true;
    }
  } else if (arg.type == ValueType::ENUM) {
    auto &token = arg.token;
    if (!isEnum(token.getText())) {
      os << getTokenError(token, os) << "User defined type: " << token.getText()
         << " not found";
      token.printLine(os);
      os << std::endl;
      validationFailed_ = true;
    }
  }
  return true;
}

bool Parser::validateServiceMethod(std::ostream &os, IdlMethod &method) {
  if (method.retType.type == ValueType::STRUCT) {
    auto &token = method.retType.token;
    if (structSet_.find(token.getText()) == structSet_.end()) {
      os << getTokenError(token, os) << "User defined type: " << token.getText()
         << " not found";
      token.printLine(os);
      os << std::endl;
      validationFailed_ = true;
    }
  } else if (method.retType.type == ValueType::DICT &&
             method.retType.second->type == ValueType::STRUCT) {
    // dict<x, target>
    auto &token = method.retType.second->token;
    if (structSet_.find(token.getText()) == structSet_.end()) {
      os << getTokenError(token, os) << "User defined type: " << token.getText()
         << " not found";
      token.printLine(os);
      os << std::endl;
      validationFailed_ = true;
    }
  } else if (method.retType.type == ValueType::SEQ &&
             method.retType.first->type == ValueType::STRUCT) {
    auto &token = method.retType.first->token;
    if (structSet_.find(token.getText()) == structSet_.end()) {
      os << getTokenError(token, os) << "User defined type: " << token.getText()
         << " not found";
      token.printLine(os);
      os << std::endl;
      validationFailed_ = true;
    }
  }
  // Check enum
  if (method.retType.type == ValueType::SEQ &&
      method.retType.first->type == ValueType::ENUM) {
    auto &token = method.retType.first->token;
    if (!isEnum(token.getText())) {
      os << getTokenError(token, os) << "User defined type: " << token.getText()
         << " not found";
      token.printLine(os);
      os << std::endl;
      validationFailed_ = true;
    }
  } else if (method.retType.type == ValueType::DICT &&
             method.retType.first->type == ValueType::ENUM) {
    auto &token = method.retType.first->token;
    if (!isEnum(token.getText())) {
      os << getTokenError(token, os) << "User defined type: " << token.getText()
         << " not found";
      token.printLine(os);
      os << std::endl;
      validationFailed_ = true;
    }
  } else if (method.retType.type == ValueType::SET &&
             method.retType.first->type == ValueType::ENUM) {
    auto &token = method.retType.first->token;
    if (!isEnum(token.getText())) {
      os << getTokenError(token, os) << "User defined type: " << token.getText()
         << " not found";
      token.printLine(os);
      os << std::endl;
      validationFailed_ = true;
    }
  } else if (method.retType.type == ValueType::ENUM) {
    auto &token = method.retType.token;
    if (!isEnum(token.getText())) {
      os << getTokenError(token, os) << "User defined type: " << token.getText()
         << " not found";
      token.printLine(os);
      os << std::endl;
      validationFailed_ = true;
    }
  }
  for (auto &arg : method.arg_list) {
    validateMethodArgument(os, arg);
  }
  return true;
}

bool Parser::validateAllService(std::ostream &os) {
  for (auto &it : serviceMap_) {
    for (auto &method : it.second.method_list) {
      validateServiceMethod(os, method);
    }
  }
  return true;
}

bool Parser::validate(std::ostream &os) {
  if (!error_.empty()) {
    os << error_;
    return false;
  }
  validateAllStruct(os);
  validateAllService(os);
  return !validationFailed_;
}

bool Parser::isEnum(const std::string &name) {
  return (enumNameSet_.find(name) != enumNameSet_.end());
}

bool Parser::addEnum(IdlEnum &&idlEnum) {
  if (enumNameSet_.find(idlEnum.name) != enumNameSet_.end()) {
    return false;
  }
  enumNameSet_.insert(idlEnum.name);
  enumList_.emplace_back(std::move(idlEnum));
  return true;
}

Token &Parser::skipBlankNoEof(std::istream &is, Tokenizer &tokenizer) {
  while (true) {
    auto &token = tokenizer.getNext(is);
    switch (token.getType()) {
    case TokenType::TERM_BLANK:
      break;
    case TokenType::TERM_DOUBLE_SLASH:
      doSingleComment(is, tokenizer);
      break;
    case TokenType::TERM_COMMENT_BEGIN:
      doMultiComment(is, tokenizer);
      break;
    default:
      return tokenizer.getCurToken();
    }
  }
}

Token &Parser::skipNoEof(std::istream &is, Tokenizer &tokenizer) {
  while (true) {
    auto &token = tokenizer.getNext(is);
    switch (token.getType()) {
    case TokenType::TERM_DOUBLE_SLASH:
      doSingleComment(is, tokenizer);
      break;
    case TokenType::TERM_COMMENT_BEGIN:
      doMultiComment(is, tokenizer);
      break;
    default:
      return tokenizer.getCurToken();
    }
  }
}

void Parser::addStruct(IdlStruct &stru) {
  if (structSet_.find(stru.name) != structSet_.end()) {
    throw std::runtime_error("Error, struct already defined");
  }
  structSet_.insert(stru.name);
  structList_.emplace_back(std::move(stru));
}

void Parser::doWrapChunk(std::istream &is, Tokenizer &tokenizer,
                         std::ostream &os, Token &cur) {
  tokenizer.unget();
  while (true) {
    if (!doChunk(is, tokenizer, os, false)) {
      cur = tokenizer.getCurToken();
      return;
    }
  }
}

void Parser::doStruct(std::istream &is, Tokenizer &tokenizer,
                      std::ostream &os) {
  if (skipBlankNoEof(is, tokenizer).getType() != TokenType::IDENTIFIER) {
    throw std::runtime_error("Error, need a identifier name");
  }
  IdlStruct idlStruct;
  idlStruct.name = tokenizer.getCurToken().getText();
  if (skipBlankNoEof(is, tokenizer).getType() != TokenType::TERM_LEFT_BRACE) {
    throw std::runtime_error("Error, need a {, do you forgot { for a struct?");
  }
  auto &token = skipBlankNoEof(is, tokenizer);
  while (true) {
    if (token.getType() == TokenType::TERM_RIGHT_BRACE) {
      break;
    }
    doWrapChunk(is, tokenizer, os, token);
    if (token.getType() == TokenType::TERM_RIGHT_BRACE) {
      break;
    }
    auto field = doStructField(is, tokenizer, token);
    if (!field) {
      throw std::runtime_error("Error, invalid field");
    }
    if (!idlStruct.addField(std::move(field))) {
      throw std::runtime_error("Error, field name already in used");
    }
    token = skipBlankNoEof(is, tokenizer);
    if (token.getType() == TokenType::TERM_RIGHT_BRACE) {
      break;
    }
  }
  addStruct(idlStruct);
}

void Parser::doEnum(std::istream &is, Tokenizer &tokenizer) {
  if (skipBlankNoEof(is, tokenizer).getType() != TokenType::IDENTIFIER) {
    throw std::runtime_error("Error, need a identifier name");
  }
  IdlEnum idlEnum;
  idlEnum.name = tokenizer.getCurToken().getText();
  if (skipBlankNoEof(is, tokenizer).getType() != TokenType::TERM_LEFT_BRACE) {
    throw std::runtime_error("Error, need a {, do you forgot { for a enum?");
  }
  auto &token = skipBlankNoEof(is, tokenizer);
  while (true) {
    if (token.getType() == TokenType::TERM_RIGHT_BRACE) {
      break;
    }
    doEnumValue(is, tokenizer, idlEnum);
    if (token.getType() == TokenType::TERM_RIGHT_BRACE) {
      break;
    }
    if (token.getType() != TokenType::TERM_COMMA) {
      throw std::runtime_error("Error, need a ','");
    }
    token = skipBlankNoEof(is, tokenizer);
  }
  if (!addEnum(std::move(idlEnum))) {
    throw std::runtime_error("Error, duplicated enum name");
  }
}

void Parser::doEnumValue(std::istream &is, Tokenizer &tokenizer,
                         IdlEnum &idlEnum) {
  if (tokenizer.getCurToken().getType() != TokenType::IDENTIFIER) {
    throw std::runtime_error("Error, need a identifier name");
  }
  IdlEnumValue enumValue;
  enumValue.name = tokenizer.getCurToken().getText();
  auto &token = skipBlankNoEof(is, tokenizer);
  if (token.getType() == TokenType::TERM_EQUAL) {
    token = skipBlankNoEof(is, tokenizer);
    if (token.getType() != TokenType::IDENTIFIER) {
      throw std::runtime_error("Error, need a integer");
    }
    try {
      enumValue.value = std::stol(tokenizer.getCurToken().getText());
    } catch (...) {
      throw std::runtime_error("Error, need a integer");
    }
    if (!idlEnum.isValidValue(enumValue.value)) {
      throw std::runtime_error("Error, duplicated value");
    }
    skipBlankNoEof(is, tokenizer);
  } else if (token.getType() == TokenType::TERM_COMMA) {
    enumValue.value = idlEnum.getNextValue();
  } else if (token.getType() == TokenType::TERM_RIGHT_BRACE) {
    enumValue.value = idlEnum.getNextValue();
  } else {
    throw std::runtime_error("Error, need a '=', ',' or '}'");
  }
  idlEnum.enum_vec.emplace_back(enumValue);
}

ValueType Parser::getValueType(Token &cur) {
  switch (cur.getType()) {
  case TokenType::IDENTIFIER:
    if (isEnum(cur.getText())) {
      return ValueType::ENUM;
    } else {
      return ValueType::STRUCT;
    }
  case TokenType::KEYWORD_SEQ:
    return ValueType::SEQ;
  case TokenType::KEYWORD_SET:
    return ValueType::SET;
  case TokenType::KEYWORD_DICT:
    return ValueType::DICT;
  case TokenType::KEYWORD_I8:
    return ValueType::I8;
  case TokenType::KEYWORD_I16:
    return ValueType::I16;
  case TokenType::KEYWORD_I32:
    return ValueType::I32;
  case TokenType::KEYWORD_I64:
    return ValueType::I64;
  case TokenType::KEYWORD_UI8:
    return ValueType::UI8;
  case TokenType::KEYWORD_UI16:
    return ValueType::UI16;
  case TokenType::KEYWORD_UI32:
    return ValueType::UI32;
  case TokenType::KEYWORD_UI64:
    return ValueType::UI64;
  case TokenType::KEYWORD_FLOAT:
    return ValueType::FLOAT;
  case TokenType::KEYWORD_DOUBLE:
    return ValueType::DOUBLE;
  case TokenType::KEYWORD_BOOL:
    return ValueType::BOOL;
  case TokenType::KEYWORD_STRING:
    return ValueType::STRING;
  case TokenType::KEYWORD_VOID:
    return ValueType::VOID;
  default:
    return ValueType::NONE;
  }
}

bool Parser::checkArgName(const std::string &name) {
  if (name.empty()) {
    return false;
  }
  if (name[0] == '_' || isalpha(name[0])) {
    // start with _ or alpha
    for (const auto &c : name) {
      if (c != '_' && !isalnum(c)) {
        // none of _ , alpha and number
        return false;
      }
    }
  } else {
    return false;
  }
  return true;
}

void Parser::doFieldIdentifier(std::istream &is, Tokenizer &tokenizer,
                               IdlNode &field, bool withName) {
  if (isEnum(tokenizer.getCurToken().getText())) {
    field.type = ValueType::ENUM;
  } else {
    field.type = ValueType::STRUCT;
  }
  field.token = tokenizer.getCurToken();
  if (withName) {
    auto &cur = skipBlankNoEof(is, tokenizer);
    if (cur.getType() != TokenType::IDENTIFIER) {
      throw std::runtime_error("Error, need a identifier name");
    }
    field.first = new IdlNode();
    field.first->token = cur;
  }
}

void Parser::doFieldSeq(std::istream &is, Tokenizer &tokenizer, IdlNode &field,
                        bool withName) {
  field.type = ValueType::SEQ;
  auto &cur = skipBlankNoEof(is, tokenizer);
  if (cur.getType() != TokenType::TERM_TEMP_LEFT) {
    throw std::runtime_error("Error, need a <");
  }
  cur = skipBlankNoEof(is, tokenizer);
  if (field.type == ValueType::NONE) {
    throw std::runtime_error("Error, end of file");
  }
  field.first = new IdlNode();
  // field type
  field.first->token = cur;
  field.first->type = getValueType(cur);
  cur = skipBlankNoEof(is, tokenizer);
  if (cur.getType() != TokenType::TERM_TEMP_RIGHT) {
    throw std::runtime_error("Error, need a >");
  }
  if (withName) {
    cur = skipBlankNoEof(is, tokenizer);
    if (cur.getType() != TokenType::IDENTIFIER) {
      throw std::runtime_error("Error, need a identifier name");
    }
    // field name
    field.token = cur;
  }
}

void Parser::doFieldSet(std::istream &is, Tokenizer &tokenizer, IdlNode &field,
                        bool withName) {
  field.type = ValueType::SET;
  auto &cur = skipBlankNoEof(is, tokenizer);
  if (cur.getType() != TokenType::TERM_TEMP_LEFT) {
    throw std::runtime_error("Error, need a <");
  }
  cur = skipBlankNoEof(is, tokenizer);
  if (!cur) {
    throw std::runtime_error("Error, need a identifier name");
  }
  field.first = new IdlNode();
  field.first->type = getValueType(cur);
  field.first->token = cur;
  if (!isContKey(field.first->type)) {
    throw std::runtime_error("Error, cannot be a key of set");
  }
  if (field.first->type == ValueType::NONE) {
    throw std::runtime_error("Error, end of file");
  }
  cur = skipBlankNoEof(is, tokenizer);
  if (cur.getType() != TokenType::TERM_TEMP_RIGHT) {
    throw std::runtime_error("Error, need a >");
  }
  if (withName) {
    cur = skipBlankNoEof(is, tokenizer);
    if (cur.getType() != TokenType::IDENTIFIER) {
      throw std::runtime_error("Error, need a identifier name");
    }
    // field name
    field.token = cur;
  }
}

void Parser::doFieldDict(std::istream &is, Tokenizer &tokenizer, IdlNode &field,
                         bool withName) {
  field.type = ValueType::DICT;
  auto &cur = skipBlankNoEof(is, tokenizer);
  if (cur.getType() != TokenType::TERM_TEMP_LEFT) {
    throw std::runtime_error("Error, need a <");
  }
  cur = skipBlankNoEof(is, tokenizer);
  if (!cur) {
    throw std::runtime_error("Error, need a identifier name");
  }
  field.first = new IdlNode();
  field.first->type = getValueType(cur);
  if (field.first->type == ValueType::ENUM) {
    throw std::runtime_error("Error, enum cannot be a key of dict");
  }
  field.first->token = cur;
  if (!isContKey(field.first->type)) {
    throw std::runtime_error("Error, cannot be a key of dict");
  }
  field.first->token = cur;
  if (field.first->type == ValueType::NONE) {
    throw std::runtime_error("Error, end of file");
  }
  cur = skipBlankNoEof(is, tokenizer);
  if (cur.getType() != TokenType::TERM_COMMA) {
    throw std::runtime_error("Error, need a ,");
  }
  field.second = new IdlNode();
  field.second->token = cur;
  cur = skipBlankNoEof(is, tokenizer);
  field.second->type = getValueType(cur);
  if (field.second->type == ValueType::NONE) {
    throw std::runtime_error("Error, end of file");
  }
  field.second->token = cur;
  cur = skipBlankNoEof(is, tokenizer);
  if (cur.getType() != TokenType::TERM_TEMP_RIGHT) {
    throw std::runtime_error("Error, need a >");
  }
  if (withName) {
    cur = skipBlankNoEof(is, tokenizer);
    if (cur.getType() != TokenType::IDENTIFIER) {
      throw std::runtime_error("Error, need a identifier name");
    }
    field.token = cur;
  }
}

/**
 * @brief检查struct 字段中是否含有大写字母
 */
auto checkFieldUpper(std::string &field) -> bool {
  bool has_upper{false};
  for (char &c : field) {
    if (c >= 'A' && c <= 'Z') {
      c = c - 'A' + 'a';
      has_upper = true;
    }
  }
  return has_upper;
}

bool isnumber(const std::string &s) {
  std::regex pattern("-[0-9]+(.[0-9]+)?|[0-9]+(.[0-9]+)?");
  if (std::regex_match(s, pattern)) {
    return true;
  }
  return false;
}

bool isinteger(const std::string &s) {
  std::regex pattern("^(0|[1-9][0-9]*)$");
  if (std::regex_match(s, pattern)) {
    return true;
  }
  return false;
}

IdlNode Parser::doStructField(std::istream &is, Tokenizer &tokenizer,
                              Token &cur) {
  IdlNode field;
  switch (cur.getType()) {
  case TokenType::IDENTIFIER:
    doFieldIdentifier(is, tokenizer, field, true);
    break;
  case TokenType::KEYWORD_SEQ:
    doFieldSeq(is, tokenizer, field, true);
    break;
  case TokenType::KEYWORD_SET:
    doFieldSet(is, tokenizer, field, true);
    break;
  case TokenType::KEYWORD_DICT:
    doFieldDict(is, tokenizer, field, true);
    break;
  case TokenType::KEYWORD_ENUM:
    doFieldIdentifier(is, tokenizer, field, true);
    break;
  default:
    field.type = getValueType(cur);
    cur = skipBlankNoEof(is, tokenizer);
    if (cur.getType() != TokenType::IDENTIFIER) {
      throw std::runtime_error("Error, need a identifier name");
    }
    // FIXME 因为pb会把结构体生成
    // 字段名称变为全小写，所以这里做了下检查，如果有大写的字段，
    // 自动转换为小写并且抛一个警告

    if (checkFieldUpper(cur.mutableText())) {
      std::cout << "[Warn] file: " << cur.getFile() << ":" << cur.getLineno()
                << " " << cur.getText() << "has uppper character" << std::endl;
    }
    field.token = cur;
    cur = skipBlankNoEof(is, tokenizer);
    if (cur.getType() != TokenType::TERM_EQUAL) {
      tokenizer.unget();
    } else {
      //
      // Get default value
      //
      doFieldDefaultValue(is, field, tokenizer, cur);
    }
    break;
  }
  return field;
}

auto checkRange(ValueType type, const std::string &value_str) -> bool {
  switch (type) {
  case ValueType::I8: {
    auto value = std::stoll(value_str);
    return (std::numeric_limits<std::int8_t>::min() <= value &&
            value <= std::numeric_limits<std::int8_t>::max());
  }
  case ValueType::I16: {
    auto value = std::stoll(value_str);
    return (std::numeric_limits<std::int16_t>::min() <= value &&
            value <= std::numeric_limits<std::int16_t>::max());
  }
  case ValueType::I32: {
    auto value = std::stoll(value_str);
    return (std::numeric_limits<std::int32_t>::min() <= value &&
            value <= std::numeric_limits<std::int32_t>::max());
  }
  case ValueType::I64: {
    auto value = std::stoll(value_str);
    return (std::numeric_limits<std::int64_t>::min() <= value &&
            value <= std::numeric_limits<std::int64_t>::max());
  }
  case ValueType::UI8: {
    auto value = std::stoull(value_str);
    return (std::numeric_limits<std::uint8_t>::min() <= value &&
            value <= std::numeric_limits<std::uint8_t>::max());
  }
  case ValueType::UI16: {
    auto value = std::stoull(value_str);
    return (std::numeric_limits<std::uint16_t>::min() <= value &&
            value <= std::numeric_limits<std::uint16_t>::max());
  }
  case ValueType::UI32: {
    auto value = std::stoull(value_str);
    return (std::numeric_limits<std::uint32_t>::min() <= value &&
            value <= std::numeric_limits<std::uint32_t>::max());
  }
  case ValueType::UI64: {
    auto value = std::stoull(value_str);
    return (std::numeric_limits<std::uint64_t>::min() <= value &&
            value <= std::numeric_limits<std::uint64_t>::max());
  }
  case ValueType::FLOAT: {
    auto value = std::stod(value_str);
    return ((double)std::numeric_limits<float>::min() < value &&
            value < (double)std::numeric_limits<float>::max());
  }
  case ValueType::DOUBLE: {
    auto value = std::stod(value_str);
    return (std::numeric_limits<double>::min() < value &&
            value < std::numeric_limits<double>::max());
  }
  default:
    break;
  }
  return false;
}

void Parser::doFieldDefaultValue(std::istream &is, IdlNode &field,
                                 Tokenizer &tokenizer, Token &cur) {
  cur = skipBlankNoEof(is, tokenizer);
  switch (field.type) {
  case ValueType::I8:
  case ValueType::I16:
  case ValueType::I32:
  case ValueType::I64:
  case ValueType::UI8:
  case ValueType::UI16:
  case ValueType::UI32:
  case ValueType::UI64: {
    if (isinteger(cur.getText())) {
      field.default_value = std::make_unique<std::string>(cur.getText());
      if (!checkRange(field.type, *field.default_value)) {
        throw std::runtime_error("Error, number out of range");
      }
    } else {
      throw std::runtime_error("Error, need a integral number");
    }
  } break;
  case ValueType::FLOAT:
  case ValueType::DOUBLE: {
    std::string float_str;
    while (cur.getType() == TokenType::IDENTIFIER ||
           cur.getType() == TokenType::TERM_DOT) {
      float_str += cur.getText();
      cur = skipNoEof(is, tokenizer);
    }
    if (cur.getType() != TokenType::IDENTIFIER &&
        cur.getType() != TokenType::TERM_DOT) {
      tokenizer.unget();
    }
    if (isnumber(float_str)) {
      field.default_value = std::make_unique<std::string>(float_str);
      if (!checkRange(field.type, *field.default_value)) {
        throw std::runtime_error("Error, number out of range");
      }
    } else {
      throw std::runtime_error("Error, need a number");
    }
  } break;
  case ValueType::BOOL: {
    if ("true" == cur.getText() || "false" == cur.getText()) {
      field.default_value = std::make_unique<std::string>(cur.getText());
    } else {
      throw std::runtime_error("Error, need a boolean value 'true' or 'false'");
    }
  } break;
  case ValueType::STRING: {
    std::string str_value;
    if (cur.getType() != TokenType::TERM_QUOTE) {
      throw std::runtime_error("Error, need a '\"'");
    }
    cur = skipNoEof(is, tokenizer);
    while (cur.getType() != TokenType::TERM_QUOTE) {
      str_value += cur.getText();
      cur = skipNoEof(is, tokenizer);
    }
    field.default_value = std::make_unique<std::string>(str_value);
  } break;
  default:
    throw std::runtime_error("Error, only basic type allow default value. "
                             "decimal, string and bool");
    break;
  }
}

IdlServiceNotation Parser::doNotation(std::istream &is, Tokenizer &tokenizer,
                                      Token &cur) {
  IdlServiceNotation notation;
  std::string content;
  auto &token = skipBlankNoEof(is, tokenizer);
  while (token && token.getType() != TokenType::TERM_RIGHT_BRACKET) {
    content += token.getText();
    token = skipBlankNoEof(is, tokenizer);
  }
  if (!token) {
    throw std::runtime_error("Error, need a ']', do you forget a ']'?");
  }
  std::vector<std::string> result;
  split(content, ":", result);
  if (!result.empty() && result.size() == 1) {
    notation.key = result[0];
  } else if (!result.empty() && result.size() == 2) {
    notation.key = result[0];
    std::string value = result[1];
    split(value, ",", result);
    for (auto &s : result) {
      notation.values.push_back(s);
    }
    if (result.empty()) {
      notation.values.push_back(value);
    }
  } else {
    throw std::runtime_error("Error, invalid notation format");
  }
  return notation;
}

void Parser::doService(std::istream &is, Tokenizer &tokenizer,
                       std::ostream &os) {
  IdlService service;
  auto &token = skipBlankNoEof(is, tokenizer);
  if (token.getType() == TokenType::KEYWORD_STATIC) {
    service.loadType = ServiceLoadType::STATIC;
    token = skipBlankNoEof(is, tokenizer);
  } else if (token.getType() == TokenType::KEYWORD_DYNAMIC) {
    service.loadType = ServiceLoadType::DYNAMIC;
    token = skipBlankNoEof(is, tokenizer);
  }
  if (token.getType() != TokenType::IDENTIFIER) {
    throw std::runtime_error(
        "Error, need a identifier name, do you mean a service?");
  }
  service.fileName = curFile_;
  service.name = tokenizer.getCurToken().getText();
  service.uuid = frontend_->makeServiceUUID(curFile_, service.name);
  token = skipBlankNoEof(is, tokenizer);
  if (token.getType() == TokenType::KEYWORD_MULTIPLE) {
    service.type = ServiceType::MULTIPLE;
    token = skipBlankNoEof(is, tokenizer);
    if (token.getType() == TokenType::TERM_EQUAL) {
      token = skipBlankNoEof(is, tokenizer);
      // TODO check is number
      service.maxInst = std::stoul(token.getText());
      token = skipBlankNoEof(is, tokenizer);
    }
  } else if (token.getType() == TokenType::KEYWORD_SINGLE) {
    service.type = ServiceType::SINGLE;
    token = skipBlankNoEof(is, tokenizer);
  } else if (token.getType() == TokenType::KEYWORD_REENTRANT) {
    service.type = ServiceType::REENTRANT;
    token = skipBlankNoEof(is, tokenizer);
  } else if (token.getType() == TokenType::KEYWORD_GENERIC) {
    service.type = ServiceType::GENERIC;
    token = skipBlankNoEof(is, tokenizer);
  }
  if (token.getType() != TokenType::TERM_LEFT_BRACE) {
    throw std::runtime_error("Error, need a {");
  }
  token = skipBlankNoEof(is, tokenizer);
  while (true) {
    if (token.getType() == TokenType::TERM_RIGHT_BRACE) {
      break;
    }
    doWrapChunk(is, tokenizer, os, token);
    if (token.getType() == TokenType::TERM_RIGHT_BRACE) {
      serviceMap_.emplace(service.name, std::move(service));
      return;
    }
    if (token.getType() == TokenType::TERM_LEFT_BRACKET) {
      auto notation = doNotation(is, tokenizer, token);
      service.service_notations.emplace(notation.key, notation);
    } else {
      auto method = doMethod(is, tokenizer, token);
      if (!service.addMethod(std::move(method))) {
        throw std::runtime_error("Error, method already defined");
      }
    }
    token = skipBlankNoEof(is, tokenizer);
    if (token.getType() == TokenType::TERM_RIGHT_BRACE) {
      break;
    }
  }
  if (serviceMap_.find(service.name) != serviceMap_.end()) {
    throw std::runtime_error("Error, service already defined");
  }
  serviceMap_.emplace(service.name, std::move(service));
}

void Parser::doPreKeyword(IdlMethod &method, std::istream &is,
                          Tokenizer &tokenizer, Token &cur) {
  while (true) {
    if (cur.getType() == TokenType::KEYWORD_ONEWAY) {
      method.oneway = true;
      cur = skipBlankNoEof(is, tokenizer);
      if (cur.getType() == TokenType::KEYWORD_VOID) {
        return;
      } else if (cur.getType() == TokenType::KEYWORD_NOEXCEPT) {
        continue;
      } else {
        throw std::runtime_error(
            "Error, oneway method must return void or other pre-keyword");
      }
    } else if (cur.getType() == TokenType::KEYWORD_NOEXCEPT) {
      method.no_except = true;
      cur = skipBlankNoEof(is, tokenizer);
      if (cur.getType() == TokenType::KEYWORD_VOID) {
        return;
      } else if (cur.getType() == TokenType::KEYWORD_ONEWAY) {
        continue;
      }
    } else if (cur.getType() == TokenType::KEYWORD_EVENT) {
      method.event = true;
      cur = skipBlankNoEof(is, tokenizer);
      return;
    } else {
      return;
    }
  }
}

IdlMethod Parser::doMethod(std::istream &is, Tokenizer &tokenizer, Token &cur) {
  IdlMethod method;
  doPreKeyword(method, is, tokenizer, cur);
  method.retType = doMethodRet(is, tokenizer, cur);
  auto &token = skipBlankNoEof(is, tokenizer);
  if ((token.getType() != TokenType::IDENTIFIER) &&
      (token.getType() != TokenType::KEYWORD_STREAM)) {
    throw std::runtime_error(
        "Error, need a identifier name or 'stream' keyword");
  }
  if (token.getType() == TokenType::KEYWORD_STREAM) {
    method.is_stream = true;
    token = skipBlankNoEof(is, tokenizer);
    if (token.getType() != TokenType::IDENTIFIER) {
      throw std::runtime_error("Error, need a identifier name");
    }
    switch (method.retType.type) {
    case ValueType::DICT:
    case ValueType::SEQ:
    case ValueType::SET:
      break;
    default:
      throw std::runtime_error("Error, streaming method's return value must be "
                               "one of 'seq', 'dict' or 'set'");
    }
  }
  method.name = token.getText();
  token = skipBlankNoEof(is, tokenizer);
  if (token.getType() != TokenType::TERM_LEFT_ROUND) {
    throw std::runtime_error("Error, need a (, do you mean a method?");
  }
  token = skipBlankNoEof(is, tokenizer);
  if (token.getType() != TokenType::TERM_RIGHT_ROUND) {
    while (true) {
      auto arg = doMethodArg(is, tokenizer, token);
      if (arg.name) {
        for (const auto &got_arg : method.arg_list) {
          // check same name
          if (got_arg.name) {
            if (*got_arg.name == *arg.name) {
              throw std::runtime_error("Error, argument name already used");
            }
            if (!checkArgName(*arg.name)) {
              throw std::runtime_error("Error, argument name is invalid");
            }
          }
        }
      }
      method.arg_list.emplace_back(std::move(arg));
      if (tokenizer.getCurToken().getType() == TokenType::TERM_RIGHT_ROUND) {
        break;
      }
      token = skipBlankNoEof(is, tokenizer);
    }
  }

  while (true) {
    token = skipBlankNoEof(is, tokenizer);
    if (token.getType() == TokenType::KEYWORD_TIMEOUT) {
      if (method.oneway) {
        throw std::runtime_error("Error, oneway method no timeout attribute");
      }
      token = skipBlankNoEof(is, tokenizer);
      if (token.getType() != TokenType::TERM_EQUAL) {
        throw std::runtime_error("Error, need a =");
      }
      token = skipBlankNoEof(is, tokenizer);
      if (token.getType() != TokenType::IDENTIFIER) {
        throw std::runtime_error("Error, need timeout in second");
      }
      try {
        method.timeout = std::stoi(token.getText());
      } catch (std::exception &) {
        throw std::runtime_error(
            "Error, timeout must be a number in [0, maximum int]");
      }
      if (method.timeout < 0) {
        throw std::runtime_error(
            "Error, timeout must be a number larger than zero");
      }
    } else if (token.getType() == TokenType::KEYWORD_RETRY) {
      if (method.oneway) {
        throw std::runtime_error("Error, oneway method no retry attribute");
      }
      token = skipBlankNoEof(is, tokenizer);
      if (token.getType() != TokenType::TERM_EQUAL) {
        throw std::runtime_error("Error, need a =");
      }
      token = skipBlankNoEof(is, tokenizer);
      if (token.getType() != TokenType::IDENTIFIER) {
        throw std::runtime_error("Error, need retry count");
      }
      try {
        method.retry = std::stoi(token.getText());
      } catch (std::exception &) {
        throw std::runtime_error(
            "Error, retry must be a number in [0, maximum int]");
      }
      if (method.retry < 0) {
        throw std::runtime_error(
            "Error, retry must be a number larger than zero");
      }
    } else {
      tokenizer.unget();
      break;
    }
  }
  return method;
}

IdlNode Parser::doMethodRet(std::istream &is, Tokenizer &tokenizer,
                            Token &cur) {
  IdlNode retType;
  if (isEnum(cur.getText())) {
    retType.type = ValueType::ENUM;
    retType.token = cur;
  } else if (cur.getType() == TokenType::IDENTIFIER) {
    retType.type = ValueType::STRUCT;
    retType.token = cur;
  } else {
    retType.type = getValueType(cur);
    switch (retType.type) {
    case ValueType::SEQ:
      doFieldSeq(is, tokenizer, retType, false);
      break;
    case ValueType::SET:
      doFieldSet(is, tokenizer, retType, false);
      break;
    case ValueType::DICT:
      doFieldDict(is, tokenizer, retType, false);
      break;
    default:
      retType.token = cur;
      break;
    }
  }
  return retType;
}

IdlNode Parser::doMethodArg(std::istream &is, Tokenizer &tokenizer,
                            Token &cur) {
  IdlNode arg;
  if (isEnum(cur.getText())) {
    arg.type = ValueType::ENUM;
    arg.token = cur;
  } else if (cur.getType() == TokenType::IDENTIFIER) {
    arg.type = ValueType::STRUCT;
    arg.token = cur;
  } else {
    arg.type = getValueType(cur);
    switch (arg.type) {
    case ValueType::SEQ:
      doFieldSeq(is, tokenizer, arg, false);
      break;
    case ValueType::SET:
      doFieldSet(is, tokenizer, arg, false);
      break;
    case ValueType::DICT:
      doFieldDict(is, tokenizer, arg, false);
      break;
    default:
      arg.token = cur;
      break;
    }
  }
  auto &token = skipBlankNoEof(is, tokenizer);
  if (token.getType() == TokenType::TERM_COMMA) {
    return arg;
  } else if (token.getType() == TokenType::TERM_RIGHT_ROUND) {
    return arg;
  } else if (token.getType() == TokenType::IDENTIFIER) {
    arg.name = std::make_unique<std::string>(token.getText());
    token = skipBlankNoEof(is, tokenizer);
    if (token.getType() == TokenType::TERM_COMMA) {
      return arg;
    } else if (token.getType() == TokenType::TERM_RIGHT_ROUND) {
      return arg;
    } else {
      throw std::runtime_error("Error, need a , or )");
    }
  } else {
    throw std::runtime_error("Error, need a , or )");
  }
}

void Parser::doSingleComment(std::istream &is, Tokenizer &tokenizer) {
  tokenizer.ignore();
  while (true) {
    auto &token = tokenizer.getNext(is);
    if (!token) {
      tokenizer.normal();
      return;
    }
    if ((token.getType() == TokenType::TERM_BLANK) &&
        (token.getText() == "\n")) {
      tokenizer.normal();
      return;
    }
  }
  tokenizer.normal();
}

void Parser::doMultiComment(std::istream &is, Tokenizer &tokenizer) {
  tokenizer.ignore();
  while (true) {
    auto &token = tokenizer.getNext(is);
    if (!token) {
      throw std::runtime_error("Error, end of file");
    }
    if (token.getType() == TokenType::TERM_COMMENT_END) {
      tokenizer.normal();
      return;
    }
  }
  tokenizer.normal();
}

void Parser::doImport(std::istream &is, Tokenizer &tokenizer,
                      std::ostream &os) {
  tokenizer.ignore();
  if (tokenizer.getNext(is).getType() != TokenType::KEYWORD_IMPORT) {
    throw std::runtime_error("Error, need import");
  }
  if (skipBlankNoEof(is, tokenizer).getType() != TokenType::TERM_QUOTE) {
    throw std::runtime_error("Error, need a \"");
  }
  std::string name;
  while (true) {
    tokenizer.ignore();
    const auto &token = tokenizer.getNext(is);
    if (token.getType() == TokenType::TERM_QUOTE) {
      tokenizer.normal();
      break;
    } else {
      name += token.getText();
    }
  }
  // TODO  Check if same file
  auto it = fileSet_.find(name);
  if (it != fileSet_.end()) {
    tokenizer.unget();
    throw std::runtime_error("Error, multi-import");
  } else {
    fileSet_.emplace(name);
  }
  if (!frontend_->updateDependency(name)) {
    tokenizer.unget();
    throw std::runtime_error("Error, multi-import");
  }
  std::string temp = curFile_;
  Tokenizer *last = curTokenizer_;
  for (const std::string &searchpath : includePathlist_) {
    if (analyze_unsafe(name, os, searchpath)) {
      break;
    }
  }
  curFile_ = temp;
  curTokenizer_ = last;
  tokenizer.normal();
}
